import org.hyperledger.fabric.sdk.Orderer;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric_ca.sdk.HFCAClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.InvalidKeySpecException;
import java.util.*;

/**
 * Created by xuguangyuansh on 2018/8/24.
 */
public class FabricOrg {

    private static final Logger logger = LoggerFactory.getLogger(FabricOrg.class);

    /** 名称 */
    private String name;
    /** 会员ID */
    private String mspId;
    /** ca 客户端 */
    private HFCAClient caClient;

    /** 用户集合 */
    Map<String, User> userMap = new HashMap<>();
    /** 本地节点集合 */
    Map<String, String> peerLocations = new HashMap<>();
    /** 本地排序服务集合 */
    Map<String, String> ordererLocations = new HashMap<>();
    /** 本地事件集合 */
    Map<String, String> eventHubLocations = new HashMap<>();

    /** 节点集合 */
    Set<Peers.Peer> peers = new HashSet<>();
    /** 联盟管理员用户 */
    private FabricUser admin;
    /** 本地 ca */
    private String caLocation;
    /** ca 配置 */
    private Properties caProperties = null;

    /** 联盟单节点管理员用户 */
    private FabricUser peerAdmin;

    /** 域名名称 */
    private String domainName;

    public FabricOrg(String username, Peers peers, Orderers orderers, FabricStore fabricStore, String cryptoConfigPath)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException {
        this.name = peers.getOrgName();
        this.mspId = peers.getOrgMSPID();
        for (Peers.Peer peer : peers.get()) {
            addPeerLocation(peer.getPeerName(), peer.getPeerLocation());
            addEventHubLocation(peer.getPeerEventHubName(), peer.getPeerEventHubLocation());
            setCALocation(peer.getCaLocation());
        }
        for (Orderers.Orderer orderer : orderers.get()) {
            addOrdererLocation(orderer.getOrdererName(), orderer.getOrdererLocation());
        }
        setDomainName(peers.getOrgDomainName());    // domainName = tk.anti-moth.com

        // Set up HFCA for Org1
        // setCAClient(HFCAClient.createNewInstance(peers.getCaLocation(), getCAProperties()));
        // setAdmin(fabricStore.getMember("admin", peers.getOrgName()));    // 设置该组织的管理员
        File skFile = Paths.get(cryptoConfigPath, "/peerOrganizations/", peers.getOrgDomainName(),
                String.format("/users/%s@%s/msp/keystore", username, peers.getOrgDomainName())).toFile();
        File certificateFile = Paths.get(cryptoConfigPath, "/peerOrganizations/", peers.getOrgDomainName(),
                String.format("/users/%s@%s/msp/signcerts/%s@%s-cert.pem", username, peers.getOrgDomainName(),
                        username, peers.getOrgDomainName())).toFile();
        logger.debug("skFile = " + skFile.getAbsolutePath());
        logger.debug("certificateFile = " + certificateFile.getAbsolutePath());
        setPeerAdmin(fabricStore.getMember(peers.getOrgName() + username, peers.getOrgName(), peers.getOrgMSPID(),
                findFileSk(skFile), certificateFile));   // 一个特殊的用户，可以创建通道，连接对等点，并安装链码
    }

    /**
     * 添加本地节点
     *
     * @param name 节点key
     * @param location 节点
     */
    public void addPeerLocation(String name, String location) {
        peerLocations.put(name, location);
    }

    /**
     * 添加本地组织
     *
     * @param name 组织key
     * @param location 组织
     */
    public void addOrdererLocation(String name, String location) {
        ordererLocations.put(name, location);
    }

    /**
     * 添加本地事件
     *
     * @param name 事件key
     * @param location 事件
     */
    public void addEventHubLocation(String name, String location) {
        eventHubLocations.put(name, location);
    }

    /**
     * 获取本地节点
     *
     * @param name 节点key
     * @return 节点
     */
    public String getPeerLocation(String name) {
        return peerLocations.get(name);
    }

    /**
     * 获取本地组织
     *
     * @param name 组织key
     * @return 组织
     */
    public String getOrdererLocation(String name) {
        return ordererLocations.get(name);
    }

    /**
     * 获取本地事件
     *
     * @param name 事件key
     * @return 事件
     */
    public String getEventHubLocation(String name) {
        return eventHubLocations.get(name);
    }

    /**
     * 获取一个不可修改的本地节点key集合
     *
     * @return 节点key集合
     */
    public Set<String> getPeerNames() {
        return Collections.unmodifiableSet(peerLocations.keySet());
    }

    /**
     * 获取一个不可修改的本地组织 key 集合
     *
     * @return
     */
    public Set<String> getOrdererNames() {
        return Collections.unmodifiableSet(ordererLocations.keySet());
    }

    /**
     * 获取一个不可修改的本地事件 key 集合
     *
     * @return 事件 key 集合
     */
    public Set<String> getEventHubNames() {
        return Collections.unmodifiableSet(eventHubLocations.keySet());
    }

    /**
     * 获取一个不可修改的本地事件集合
     *
     * @return 事件集合
     */
    public Collection<String> getEventHubLocations() {
        return Collections.unmodifiableCollection(eventHubLocations.values());
    }

    /**
     * 获取一个不可修改的本地组织集合
     *
     * @return 组织集合
     */
    public Collection<String> getOrdererLocations() {
        return Collections.unmodifiableCollection(ordererLocations.values());
    }

    /**
     * 获取一个不可修改的本地节点集合
     *
     * @return 集合
     */
    public Set<Peers.Peer> getPeers() {
        return Collections.unmodifiableSet(peers);
    }

    /**
     * 向用户集合中添加用户
     *
     * @param user 用户
     */
    public void addUser(FabricUser user) {
        userMap.put(user.getName(), user);
    }

    /**
     * 从用户集合根据名称获取用户
     *
     * @param name 名称
     * @return 用户
     */
    public User getUser(String name) {
        return userMap.get(name);
    }

    /**
     * 向节点集合中添加节点
     *
     * @param peer 节点
     */
    public void addPeer(Peers.Peer peer) {
        peers.add(peer);
    }

    public String getCaLocation() {
        return caLocation;
    }

    public void setCALocation(String caLocation) {
        this.caLocation = caLocation;
    }

    public FabricUser getPeerAdmin() {
        return peerAdmin;
    }

    public void setPeerAdmin(FabricUser peerAdmin) {
        this.peerAdmin = peerAdmin;
    }

    public String getDomainName() {
        return domainName;
    }

    public void setDomainName(String domainName) {
        this.domainName = domainName;
    }

    /**
     * 从指定路径中获取后缀为 _sk 的文件，且该路径下有且仅有该文件
     *
     * @param directory 指定路径
     * @return File
     */
    private File findFileSk(File directory) {
        logger.warn("### directory: ", directory);
        File[] matches = directory.listFiles((dir, name) -> name.endsWith("_sk"));
        if (null == matches) {
            throw new RuntimeException(String.format("Matches returned null does %s directory exist?",
                    directory.getAbsoluteFile().getName()));
        }
        if (matches.length > 1) {
            throw new RuntimeException(String.format("Expected in %s only 1 sk file but found %d",
                    directory.getAbsoluteFile().getName(), matches.length));
        }
        return matches[0];
    }
}
